#ifndef TIEC_TREE_PRINTER_H
#define TIEC_TREE_PRINTER_H

#include <sstream>
#include "macro.h"
#include "tree.h"

namespace NS_TIEC {
  /// 打印输出时的上下文
  struct PrintEnv {
    std::ostringstream stream;
    size_t indent{0};
  };

  /// 语法树转为结绳代码的打印输出器
  class TreePrinter : public TreeVisitor<void, PrintEnv&> {
  public:
    /// 将语法树输出为结绳代码文本
    /// @param tree 语法树
    String printAsCode(const PTR<Tree>& tree);

    void visitCompilationUnitTree(const PTR<CompilationUnitTree>& tree, PrintEnv& param) override;
    void visitClassTree(const PTR<ClassTree>& tree, PrintEnv& param) override;
    void visitMethodTree(const PTR<MethodTree>& tree, PrintEnv& param) override;
    void visitEventTree(const PTR<EventTree>& tree, PrintEnv& param) override;
    void visitVariableTree(const PTR<VariableTree>& tree, PrintEnv& param) override;
    void visitAnnotationTree(const PTR<AnnotationTree>& tree, PrintEnv& param) override;
    void visitBlockTree(const PTR<BlockTree>& tree, PrintEnv& param) override;
    void visitIfTree(const PTR<IfTree>& tree, PrintEnv& param) override;
    void visitSwitchTree(const PTR<SwitchTree>& tree, PrintEnv& param) override;
    void visitCaseTree(const PTR<CaseTree>& tree, PrintEnv& param) override;
    void visitForLoopTree(const PTR<ForLoopTree>& tree, PrintEnv& param) override;
    void visitForEachTree(const PTR<ForEachTree>& tree, PrintEnv& param) override;
    void visitWhileLoopTree(const PTR<WhileLoopTree>& tree, PrintEnv& param) override;
    void visitExpressionStatementTree(const PTR<ExpressionStatementTree>& tree, PrintEnv& param) override;
    void visitBinaryTree(const PTR<BinaryTree>& tree, PrintEnv& param) override;
    void visitUnaryTree(const PTR<UnaryTree>& tree, PrintEnv& param) override;
    void visitIdentifierTree(const PTR<IdentifierTree>& tree, PrintEnv& param) override;
    void visitLiteralTree(const PTR<LiteralTree>& tree, PrintEnv& param) override;
    void visitFieldAccessTree(const PTR<FieldAccessTree>& tree, PrintEnv& param) override;
    void visitMethodInvokeTree(const PTR<MethodInvokeTree>& tree, PrintEnv& param) override;
    void visitArrayAccessTree(const PTR<ArrayAccessTree>& tree, PrintEnv& param) override;
    void visitNewClassTree(const PTR<NewClassTree>& tree, PrintEnv& param) override;
    void visitNewArrayTree(const PTR<NewArrayTree>& tree, PrintEnv& param) override;
    void visitAssignTree(const PTR<AssignTree>& tree, PrintEnv& param) override;
    void visitExchangeTree(const PTR<ExchangeTree>& tree, PrintEnv& param) override;
    void visitTypeCastTree(const PTR<TypeCastTree>& tree, PrintEnv& param) override;
    void visitInstanceOfTree(const PTR<InstanceOfTree>& tree, PrintEnv& param) override;
    void visitParensTree(const PTR<ParensTree>& tree, PrintEnv& param) override;
    void visitMultiExprTree(const PTR<MultiExprTree>& tree, PrintEnv& param) override;
    void visitAwaitTree(const PTR<AwaitTree>& tree, PrintEnv& param) override;
    void visitBreakTree(const PTR<BreakTree>& tree, PrintEnv& param) override;
    void visitContinueTree(const PTR<ContinueTree>& tree, PrintEnv& param) override;
    void visitReturnTree(const PTR<ReturnTree>& tree, PrintEnv& param) override;
    void visitSubscribeEventTree(const PTR<SubscribeEventTree>& tree, PrintEnv& param) override;
    void visitArrayTypeTree(const PTR<ArrayTypeTree>& tree, PrintEnv& param) override;
    void visitTemplateTypeTree(const PTR<TemplateTypeTree>& tree, PrintEnv& param) override;
    void visitCodeSegmentTree(const PTR<CodeSegmentTree>& tree, PrintEnv& param) override;
    void visitCodeExpressionTree(const PTR<CodeExpressionTree>& tree, PrintEnv& param) override;
    void visitCodeStatementTree(const PTR<CodeStatementTree>& tree, PrintEnv& param) override;
    void visitCodeTextTree(const PTR<CodeTextTree>& tree, PrintEnv& param) override;
    void visitCodeRefTree(const PTR<CodeRefTree>& tree, PrintEnv& param) override;
    void visitErroneousTree(const PTR<ErroneousTree>& tree, PrintEnv& param) override;

  private:
    void printAnnotations(const PTR<AnnotatedTree>& tree, PrintEnv& env);
    static void printText(const char* text, PrintEnv& env);
    static void printText(const String& text, PrintEnv& env);
    static void printName(const PTR<Name>& name, PrintEnv& env);
    static void printLine(PrintEnv& env);
    static void printTab(PrintEnv& env);
    static void pushIndent(PrintEnv& env);
    static void popIndent(PrintEnv& env);
    static const char* getOperatorText(const TiecodeToken& token);
  };

  /// 用于调试的语法树打印输出器，会按明显的树形结构展示语法结构
  class TreeDebugPrinter : public TreeVisitor<void, PrintEnv&> {
  public:
    void debugPrint(const PTR<Tree>& tree, std::ostream& out = std::cout);

    void visitCompilationUnitTree(const PTR<CompilationUnitTree>& tree, PrintEnv& env) override;
    void visitClassTree(const PTR<ClassTree>& tree, PrintEnv& env) override;
    void visitMethodTree(const PTR<MethodTree>& tree, PrintEnv& env) override;
    void visitEventTree(const PTR<EventTree>& tree, PrintEnv& env) override;
    void visitVariableTree(const PTR<VariableTree>& tree, PrintEnv& env) override;
    void visitAnnotationTree(const PTR<AnnotationTree>& tree, PrintEnv& env) override;
    void visitBlockTree(const PTR<BlockTree>& tree, PrintEnv& env) override;
    void visitIfTree(const PTR<IfTree>& tree, PrintEnv& env) override;
    void visitSwitchTree(const PTR<SwitchTree>& tree, PrintEnv& env) override;
    void visitCaseTree(const PTR<CaseTree>& tree, PrintEnv& env) override;
    void visitForLoopTree(const PTR<ForLoopTree>& tree, PrintEnv& env) override;
    void visitForEachTree(const PTR<ForEachTree>& tree, PrintEnv& env) override;
    void visitWhileLoopTree(const PTR<WhileLoopTree>& tree, PrintEnv& env) override;
    void visitExpressionStatementTree(const PTR<ExpressionStatementTree>& tree, PrintEnv& env) override;
    void visitBinaryTree(const PTR<BinaryTree>& tree, PrintEnv& env) override;
    void visitUnaryTree(const PTR<UnaryTree>& tree, PrintEnv& env) override;
    void visitIdentifierTree(const PTR<IdentifierTree>& tree, PrintEnv& env) override;
    void visitLiteralTree(const PTR<LiteralTree>& tree, PrintEnv& env) override;
    void visitFieldAccessTree(const PTR<FieldAccessTree>& tree, PrintEnv& env) override;
    void visitMethodInvokeTree(const PTR<MethodInvokeTree>& tree, PrintEnv& env) override;
    void visitArrayAccessTree(const PTR<ArrayAccessTree>& tree, PrintEnv& env) override;
    void visitNewClassTree(const PTR<NewClassTree>& tree, PrintEnv& env) override;
    void visitNewArrayTree(const PTR<NewArrayTree>& tree, PrintEnv& env) override;
    void visitAssignTree(const PTR<AssignTree>& tree, PrintEnv& env) override;
    void visitExchangeTree(const PTR<ExchangeTree>& tree, PrintEnv& env) override;
    void visitTypeCastTree(const PTR<TypeCastTree>& tree, PrintEnv& env) override;
    void visitInstanceOfTree(const PTR<InstanceOfTree>& tree, PrintEnv& env) override;
    void visitParensTree(const PTR<ParensTree>& tree, PrintEnv& env) override;
    void visitMultiExprTree(const PTR<MultiExprTree>& tree, PrintEnv& env) override;
    void visitAwaitTree(const PTR<AwaitTree>& tree, PrintEnv& env) override;
    void visitBreakTree(const PTR<BreakTree>& tree, PrintEnv& env) override;
    void visitContinueTree(const PTR<ContinueTree>& tree, PrintEnv& env) override;
    void visitReturnTree(const PTR<ReturnTree>& tree, PrintEnv& env) override;
    void visitSubscribeEventTree(const PTR<SubscribeEventTree>& tree, PrintEnv& env) override;
    void visitArrayTypeTree(const PTR<ArrayTypeTree>& tree, PrintEnv& env) override;
    void visitTemplateTypeTree(const PTR<TemplateTypeTree>& tree, PrintEnv& env) override;
    void visitCodeSegmentTree(const PTR<CodeSegmentTree>& tree, PrintEnv& env) override;
    void visitCodeExpressionTree(const PTR<CodeExpressionTree>& tree, PrintEnv& env) override;
    void visitCodeStatementTree(const PTR<CodeStatementTree>& tree, PrintEnv& env) override;
    void visitCodeTextTree(const PTR<CodeTextTree>& tree, PrintEnv& env) override;
    void visitCodeRefTree(const PTR<CodeRefTree>& tree, PrintEnv& env) override;
    void visitErroneousTree(const PTR<ErroneousTree>& tree, PrintEnv& env) override;

  private:
    static void printTab(PrintEnv& env);
    static void printLine(const String& text, PrintEnv& env);
    static void printField(const String& name, const String& value, PrintEnv& env);
    static void printField(const String& name, int64_t value, PrintEnv& env);
    static void printField(const String& name, bool value, PrintEnv& env);
    static void printOrigin(const Origin& origin, PrintEnv& env);
    static void pushIndent(PrintEnv& env);
    static void popIndent(PrintEnv& env);

    template <typename T>
    void printList(const String& name, List<PTR<T>>& list, PrintEnv& env) {
      if (list.empty()) return;

      printLine(name + ": [", env);
      pushIndent(env);
      for (const PTR<T>& tree : list) {
        visitTree(tree, env);
      }
      popIndent(env);
      printLine("]", env);
    }

    template <typename T>
    void printNode(const String& name, const PTR<T>& node, PrintEnv& env) {
      if (!node) return;

      printLine(name + ": ", env);
      pushIndent(env);
      if (node) {
        visitTree(node, env);
      } else {
        printLine("null", env);
      }
      popIndent(env);
    }
  };
}

#endif //TIEC_TREE_PRINTER_H
